package com.zhuyuan.auth.config;

import com.zhuyuan.core.constant.Oauth2Constants;
import com.zhuyuan.core.exception.TokenException;
import com.zhuyuan.redis.RedisService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @description: 客户端与Redis；通过redis获取客户端client信息，redis没有，读取数据库
 * @author: 张琳凯
 * @date: 2025/1/9 17:47
 **/

@Slf4j
@Setter
@Service
public class ClientDetailsServiceImpl extends JdbcClientDetailsService {

	@Autowired
	private DataSource dataSource;

	@Autowired
	private RedisService redisTemplate;

	public ClientDetailsServiceImpl(DataSource dataSource) {
		super(dataSource);
	}

	@Bean
	@Primary
	public ClientDetailsServiceImpl clientDetailService() {
		ClientDetailsServiceImpl clientDetailsService = new ClientDetailsServiceImpl(dataSource);
		clientDetailsService.setRedisTemplate(redisTemplate);
		return clientDetailsService;
	}

	/**
	 * 获取客户端信息，没redis没有，从数据库获取
	 *
	 * @param clientId 客户端ID
	 * @return ClientDetails
	 * @throws InvalidClientException 非法客户端异常
	 */
	@Override
	public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {

		ClientDetails clientDetails =  redisTemplate.getCacheObject(clientKey(clientId));
		if (null==clientDetails) {
			clientDetails = getCacheClient(clientId);
		}
		clientDetails.getAuthorizedGrantTypes().add("password");
		return clientDetails;
	}


	/**
	 * 从数据库获取客户端信息，并同步到Redis
	 *
	 * @param clientId 客户端ID
	 * @return ClientDetails
	 */
	private ClientDetails getCacheClient(String clientId) {

		ClientDetails clientDetails = null;
		try {

			clientDetails = super.loadClientByClientId(clientId);
			if (null!=clientDetails) {
				redisTemplate.setCacheObject(clientKey(clientId), clientDetails);
				log.debug("Cache clientId:{}, clientDetails:{}", clientId, clientDetails);
			}
		} catch (Exception e) {
			log.error("Exception for clientId:{}, message:{}", clientId, e.getMessage());
			log.error("客户端key或secret不存在!", e);
			throw new TokenException("客户端key或secret不存在!");
		}
		return clientDetails;
	}


	/**
	 * 缓存key
	 */
	private String clientKey(String clientId) {
		return Oauth2Constants.CLIENT_TABLE+ ":" + clientId;
	}
}
